package sfeir.authform.client.forms;

import sfeir.authform.client.forms.images.AuthImages;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasAlignment;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * Generic profile creation form. The basic is email/password You can customize
 * it as you wish, through its methods.
 * 
 * @author sfeir
 * 
 */
public abstract class CreationForm extends Composite {

    private boolean emailConfirmationEnabled = false, passwordConfirmationEnabled = true;

    private String email, password, emailConfirm, passwordConfirm;
    private HTML emailLabel, emailConfirmLabel, passwordLabel, passwordConfirmLabel;
    private TextBox emailField, emailConfirmField;
    private PasswordTextBox passwordField, passwordConfirmField;
    private Button ok;
    private FlexTable form;
    private VerticalPanel root;

    private KeyUpHandler keyHandler;
    private ClickHandler clickHandler;

    private AuthImages images;

    private final static int ROW_EMAIL = 0;
    private final static int ROW_EMAIL_CONFIRM = 1;
    private final static int ROW_PASSWD = 2;
    private final static int ROW_PASSWD_CONFIRM = 3;
    private final static int COL_LABEL = 0;
    private final static int COL_WIDGET = 1;
    private final static int COL_CHECK = 2;
    
    /**
     * Method to redefine in order to do what you want once the forms is to be sent.
     * @param login the login as it was typed by the user
     * @param passwd the md5 hash of the users's password
     */
    public abstract void sendLogin(String login, String passwd);

    /**
     * Create a basic creation form with email/password fields and its
     * confirmation fields
     * By default the password confirmaiton is activated, but not th email one.
     */
    public CreationForm() {
        root = new VerticalPanel();
        form = new FlexTable();

        /*
         * STEP 1 : we implements the handlers.
         */
        keyHandler = new KeyUpHandler() {

            @Override
            public void onKeyUp(KeyUpEvent event) {
                email = emailField.getText();
                password = passwordField.getText();
                
                if (emailConfirmationEnabled) {
                    checkLoginConfirm();
                    if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
                        sendLogin(email, password);//if you press enter, it sends the form
                    }
                }

                if (passwordConfirmationEnabled) {
                    checkPasswordConfirm();
                    if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
                        password = Natives.md5(password);//encrypt password before sending
                        sendLogin(email, password);//if you press enter, it sends the form
                    }
                }

                if (!email.isEmpty() && !password.isEmpty()) {
                    if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER){
                        password = Natives.md5(password);
                        sendLogin(email, password);
                    }
                }
            }
        };

        clickHandler = new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                email = emailField.getText().trim();
                password = passwordField.getText();
                password = Natives.md5(password);
                sendLogin(email, password);
            }
        };

        /*
         * STEP 2 : We build the basic form containg email and password fields.
         */

        // EMAIL FIELDS
        emailLabel = new HTML("Email :");
        emailField = new TextBox();
        emailField.addKeyUpHandler(keyHandler);
        emailField.setWidth("10em");
        form.setWidget(ROW_EMAIL, COL_LABEL, emailLabel);
        form.setWidget(ROW_EMAIL, COL_WIDGET, emailField);

        emailConfirmLabel = new HTML("Confirm Email :");
        emailConfirmField = new TextBox();
        emailConfirmField.addKeyUpHandler(keyHandler);
        emailConfirmField.setWidth("10em");
        form.setWidget(ROW_EMAIL_CONFIRM, COL_LABEL, emailConfirmLabel);
        form.setWidget(ROW_EMAIL_CONFIRM, COL_WIDGET, emailConfirmField);

        // PASSWORD FIELDS
        passwordLabel = new HTML("Password :");
        passwordField = new PasswordTextBox();
        passwordField.addKeyUpHandler(keyHandler);
        passwordField.setWidth("10em");
        form.setWidget(ROW_PASSWD, COL_LABEL, passwordLabel);
        form.setWidget(ROW_PASSWD, COL_WIDGET, passwordField);

        passwordConfirmLabel = new HTML("Confirm Password :");
        passwordConfirmField = new PasswordTextBox();
        passwordConfirmField.addKeyUpHandler(keyHandler);
        passwordConfirmField.setWidth("10em");
        form.setWidget(ROW_PASSWD_CONFIRM, COL_LABEL, passwordConfirmLabel);
        form.setWidget(ROW_PASSWD_CONFIRM, COL_WIDGET, passwordConfirmField);

        // CHECK IMAGES
        if (images == null)
            images = GWT.create(AuthImages.class);
        Image empty = images.empty().createImage();
        form.setWidget(ROW_EMAIL_CONFIRM, COL_CHECK, empty);
        form.setWidget(ROW_PASSWD_CONFIRM, COL_CHECK, empty);

        //BUTTON
        ok = new Button();
        ok.setText("Subscribe");
        ok.addClickHandler(clickHandler);

        //ASSEMBLING THE ELEMENTS
        root.add(form);
        root.add(ok);
        root.setCellHorizontalAlignment(ok, HasAlignment.ALIGN_RIGHT);

        setEmailConfirmationEnabled(false);//disable email confirmation

        initWidget(root);
    }

    /**
     * Create a creation form with given labels
     * 
     * @param labelLogin the text that will describe the login TextBox
     * @param labelPassword the text that will describe the password TextBox
     * @param labelButton the text that will appear in the Button (can be html)
     */
    public CreationForm(String labelLogin, String labelPassword, String labelButton) {
        this();
        emailLabel.setHTML(labelLogin);
        emailConfirmLabel.setHTML("Confirm " + labelLogin);
        passwordLabel.setHTML(labelPassword);
        passwordConfirmLabel.setHTML("Confirm " + labelPassword);
        ok.setHTML(labelButton);
    }

    /**
     * If the login confirmation as been asked, 
     * check both if the fields are empty and
     * if their are equals
     */
    private void checkLoginConfirm() {
        emailConfirm = emailConfirmField.getText();

        if (images == null)
            images = GWT.create(AuthImages.class);

        if (!email.isEmpty() && !emailConfirm.isEmpty()) {
            if (email.equalsIgnoreCase(emailConfirm)) {
                Image tick = images.tick().createImage();
                form.setWidget(ROW_EMAIL_CONFIRM, COL_CHECK, tick);
                if (passwordConfirmationEnabled)
                    checkPasswordConfirm();
            } else{
                Image cross = images.cross().createImage();
                form.setWidget(ROW_EMAIL_CONFIRM, COL_CHECK, cross);
            }
        } else {
            Image empty = images.empty().createImage();
            form.setWidget(ROW_EMAIL_CONFIRM, COL_CHECK, empty);
        }
    }

    /**
     * If the password confirmation as been asked, 
     * check both if the fields are empty and
     * if their are equals
     */
    private void checkPasswordConfirm() {
        passwordConfirm = passwordConfirmField.getText();

        if (images == null)
            images = GWT.create(AuthImages.class);

        if (!password.isEmpty() && !passwordConfirm.isEmpty()) {
            if (password.equalsIgnoreCase(passwordConfirm)) {
                Image tick = images.tick().createImage();
                form.setWidget(ROW_PASSWD_CONFIRM, COL_CHECK, tick);
            } else {
                Image cross = images.cross().createImage();
                form.setWidget(ROW_PASSWD_CONFIRM, COL_CHECK, cross);
            }
        } else {
            Image empty = images.empty().createImage();
            form.setWidget(ROW_PASSWD_CONFIRM, COL_CHECK, empty);

        }
    }
    
    /**
     * Test whither the email as the good format or not
     * @return True is the format is valid, False otherwise
     */
    public boolean checkEmailFormat() {
        email = emailField.getText();
        return (email.matches("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"));
    }

    /**
     * Either if the email typing confirmation is enabled or not
     * @return
     */
    public boolean isEmailConfirmationEnabled() {
        return emailConfirmationEnabled;
    }

    /**
     * Enable or disable the email typing confirmation field.
     * A confirmation TextBox will be automaticaly set up underneath the one concerned
     * @param emailConfirmationEnabled Set <code>true</code> if you want the check the equality of emails.
     */
    public void setEmailConfirmationEnabled(boolean emailConfirmationEnabled) {
        this.emailConfirmationEnabled = emailConfirmationEnabled;
        if (this.emailConfirmationEnabled) {
            form.getWidget(ROW_EMAIL_CONFIRM, COL_LABEL).setVisible(true);
            form.getWidget(ROW_EMAIL_CONFIRM, COL_WIDGET).setVisible(true);
        } else {
            form.getWidget(ROW_EMAIL_CONFIRM, COL_LABEL).setVisible(false);
            form.getWidget(ROW_EMAIL_CONFIRM, COL_WIDGET).setVisible(false);
        }
    }

    /**
     * Either if the password typing confirmation is enabled or not
     * @return
     */
    public boolean isPasswordConfirmationEnabled() {
        return passwordConfirmationEnabled;
    }

    /**
     * Enable or disable the password typing confirmation field.
     * A confirmation TextBox will be automaticaly set up underneath the one concerned
     * @param passwordConfirmationEnabled Set <code>true</code> if you want the check the equality of passwords.
     */
    public void setPasswordConfirmationEnabled(boolean passwordConfirmationEnabled) {
        this.passwordConfirmationEnabled = passwordConfirmationEnabled;
        if (this.passwordConfirmationEnabled) {
            form.getWidget(ROW_PASSWD_CONFIRM, COL_LABEL).setVisible(true);
            form.getWidget(ROW_PASSWD_CONFIRM, COL_WIDGET).setVisible(true);
        } else {
            form.getWidget(ROW_PASSWD_CONFIRM, COL_LABEL).setVisible(false);
            form.getWidget(ROW_PASSWD_CONFIRM, COL_WIDGET).setVisible(false);
        }
    }

    /**
     * Add your own form option with label and given widget
     * 
     * @param label the text that will describe your widget
     * @param w The widget you want to add to the form
     */
    public void addField(String title, Widget w) {

        int size = form.getRowCount();

        HTML label = new HTML(title);

        form.setWidget(size, COL_LABEL, label);
        form.setWidget(size, COL_WIDGET, w);
    }
}
